package cn.tedu._05springaop.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * Aspect注解
 * 切面类，为目标方法植入额外扩展功能的类
 *
 * Order注解:
 * 多个切面时，指定切面执行的顺序，越小越先执行
 */
@Order(10)
@Aspect
@Component
public class logAspect {
    /*
    自定义切入点表达式的方法，指定哪些目标方法需要被增强
     */
    @Pointcut("execution(public int cn.tedu._05springaop.aop.CalculatorImpl.*(..))")
    public void doTime(){}

    /**
     * Before注解，表示前置通知，在目标方法执行之前执行
     * JoinPoint表示连接对象，即拦截到的目标方法对象;
     */
    @Before("doTime()")
    public void before(JoinPoint joinPoint){
        /*
        获取目标方法的名称和参数
         */
        String name = joinPoint.getSignature().getName();
        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println("[前置通知]:"+name+"方法开始,参数为:"+args);


    }
    /**
     * After注解：后置通知，在目标方法结束后执行【正常结束|异常结束】
     */
    @After("doTime()")
    public void after(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        //后置通知没有权限获取到目标方法的返回值
        System.out.println("[后置通知]:"+name+"方法执行结束");
    }

    /**
     * AfterReturning注解:返回通知
     * 1.目标方法正常结束后执行
     * 2.有权限获取到目标方法的返回值
     */
    @AfterReturning(value = "doTime()",returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result){
        String name = joinPoint.getSignature().getName();
        System.out.println("[返回通知]:" + name + "方法执行结束，结果为:" + result);
    }

    /**
     * AgterThrowing注解:异常通知
     * 1.目标方法抛出异常时执行;
     * 2.有权限获取异常信息;
     */
    @AfterThrowing(value="doTime()",throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint,Throwable ex){
        System.out.println("[异常通知]:目标方法抛出异常,信息为:" + ex.getMessage());
    }

    /**
     * Around注解:环绕通知
     * 1.等价于 前置通知+返回通知+异常通知+后置通知
     * 2.通常包裹在 try...catch...finally...语句中
     * ProceedingJoinPoint类是JoinPoint的子类，定义了可以执行目标方法的方法
     */
    @Around("doTime()")
    public Object around(ProceedingJoinPoint joinPoint){
        Object result = null;
        try{
            System.out.println("环绕通知-前置");
            result = joinPoint.proceed();
            System.out.println("环绕通知-返回");
        }catch(Throwable e){
            System.out.println("环绕通知-异常");
            throw new RuntimeException(e);
        }finally{
            System.out.println("环绕通知-后置");
        }



        return result;

    }




}
